<!DOCTYPE html>


<html lang="cn">


<head>
  <meta charset="utf-8" />
    
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
     Fahoud
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

<link rel="alternate" href="/atom.xml" title="Fahoud" type="application/atom+xml">
<link rel="stylesheet" href="/assets/css/APlayer.min.css" class="aplayer-style-marker">
<script src="/assets/js/APlayer.min.js" class="aplayer-script-marker"></script>
</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      
<section class="cover">
    
      
      <a class="forkMe" href="https://github.com/Shen-Yu/hexo-theme-ayer"
        target="_blank"><img width="149" height="149" src="/images/forkme.png"
          class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1"></a>
    
  <div class="cover-frame">
    <div class="bg-box">
      <img src="http://fahoud.gitee.io/hexo-dist/cover/BlueSky.jpg" alt="image frame" />
    </div>
    <div class="cover-inner text-center text-white">
      <h1><a href="/">Fahoud</a></h1>
      <div id="subtitle-box">
        
        <span id="subtitle"></span>
        
      </div>
      <div>
        
      </div>
    </div>
  </div>
  <div class="cover-learn-more">
    <a href="javascript:void(0)" class="anchor"><i class="ri-arrow-down-line"></i></a>
  </div>
</section>



<script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11/lib/typed.min.js"></script>


<!-- Subtitle -->

  <script>
    try {
      var typed = new Typed("#subtitle", {
        strings: ['勤俭诚信，立己树人', '愿你一生努力，一生被爱', '想要的都拥有，得不到的都释怀'],
        startDelay: 0,
        typeSpeed: 200,
        loop: true,
        backSpeed: 100,
        showCursor: true
      });
    } catch (err) {
      console.log(err)
    }
  </script>
  
<div id="main">
  <section class="outer">
  <article class="articles">
    
    
    
    
    <article
  id="post-Metadata区容量初始值太小导致频繁fullgc的问题解决"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/26/Metadata%E5%8C%BA%E5%AE%B9%E9%87%8F%E5%88%9D%E5%A7%8B%E5%80%BC%E5%A4%AA%E5%B0%8F%E5%AF%BC%E8%87%B4%E9%A2%91%E7%B9%81fullgc%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/"
    >Metadata区容量初始值太小导致频繁fullgc的问题解决</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/26/Metadata%E5%8C%BA%E5%AE%B9%E9%87%8F%E5%88%9D%E5%A7%8B%E5%80%BC%E5%A4%AA%E5%B0%8F%E5%AF%BC%E8%87%B4%E9%A2%91%E7%B9%81fullgc%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/" class="article-date">
  <time datetime="2020-07-26T06:10:43.000Z" itemprop="datePublished">2020-07-26</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/JVM/">JVM</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h1><p>之前在修改过运行参数的时候发现，jar包每一次重新运行起来都至少要经过三次的Full GC才能正常运行</p>
<p>在问题解决之前的jvm运行参数为，JDK8：</p>
<blockquote>
<p>-server<br>-Xms512m<br>-Xmx512m<br>-XX:+PrintGCDetails<br>-XX:+PrintGCDateStamps<br>-Xloggc:/usr/local/xx/gclogs/gc.log</p>
</blockquote>
<p>Full GC原始问题</p>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/fullgcjstat.png" width=""></div>

<p>堆容量设置为512M，NewRatio参数默认为2（新生代与老年代比例为1:2），意味着老年代起码有三百多兆，才刚启动应用按理来说不会这么容易fullgc</p>
<h1 id="原因描述"><a href="#原因描述" class="headerlink" title="原因描述"></a>原因描述</h1><p>查看GC日志，发现这三次FullGC发生的原因都是因为[Full GC (Metadata GC Threshold)]</p>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/fullgclog.png" width=""></div>

<h2 id="啥是Metadata-GC-Threshold？"><a href="#啥是Metadata-GC-Threshold？" class="headerlink" title="啥是Metadata GC Threshold？"></a>啥是Metadata GC Threshold？</h2><p>根据<del>百度翻译</del>可知，Threshold指阀、界，所以发生FullGC的原因是元数据区扩容，通过jstat可以知道，元数据区容量(MC)为84504.0kb，也就是85M左右</p>
<blockquote>
<p>通过参数-XX:+PrintFlagsInitial来获取jvm初始参数</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/fullgcmeta.png" width=""></div>

<p>由此可知元数据区默认初始容量为20M左右，最大元数据区容量默认为18446744073709551615字节（假设无穷大），而应用程序从启动到启动完毕元数据区至少需要85兆字节的内存，所以应用启动完毕至少经过了三次的FULLGC</p>
<h1 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h1><h2 id="XX-MetaspaceSize-N"><a href="#XX-MetaspaceSize-N" class="headerlink" title="-XX:MetaspaceSize=N"></a>-XX:MetaspaceSize=N</h2><p>这个参数是初始化的Metaspace大小，这个值越大触发元数据区gc的时机就越晚。这个值在不同的平台上的默认值会有所不同，一般默认值在12M到20M浮动，只要把元数据区的初始容量大小调高，应用在启动过程中就不会因为元数据区容量不足而导致FullGC</p>
<blockquote>
<p>给应用添加启动参数<br>-XX:MetaspaceSize=128M<br>-server<br>-Xms512m<br>-Xmx512m<br>-XX:+PrintGCDetails<br>-XX:+PrintGCDateStamps<br>-Xloggc:/usr/local/xx/gclogs/gc.log</p>
</blockquote>
<p>FullGC消失了</p>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/fullgcresolve.png" width=""></div>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JVM/" rel="tag">JVM</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-JVM性能检测、故障检测工具"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/21/JVM%E6%80%A7%E8%83%BD%E6%A3%80%E6%B5%8B%E3%80%81%E6%95%85%E9%9A%9C%E6%A3%80%E6%B5%8B%E5%B7%A5%E5%85%B7/"
    >JVM性能检测、故障检测工具</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/21/JVM%E6%80%A7%E8%83%BD%E6%A3%80%E6%B5%8B%E3%80%81%E6%95%85%E9%9A%9C%E6%A3%80%E6%B5%8B%E5%B7%A5%E5%85%B7/" class="article-date">
  <time datetime="2020-07-21T03:35:11.000Z" itemprop="datePublished">2020-07-21</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/JVM/">JVM</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  
        <div id="aplayer-toTGtWpI" class="aplayer aplayer-tag-marker" style="margin-bottom: 20px;">
            <pre class="aplayer-lrc-content"></pre>
        </div>
        <script>
          var ap = new APlayer({
            element: document.getElementById("aplayer-toTGtWpI"),
            narrow: false,
            autoplay: false,
            showlrc: false,
            music: {
              title: "REMIND YOU(LIVE)",
              author: "小林未郁",
              url: "http://fahoud.gitee.io/hexo-dist/music/小林未郁_REMIND_YOU(LIVE).mp3",
              pic: "https://fahoud.gitee.io/hexo-dist/music/UCvol_3.jpg",
              lrc: ""
            }
          });
          window.aplayers || (window.aplayers = []);
          window.aplayers.push(ap);
        </script>

<h1 id="JPS：虚拟机进程状况工具"><a href="#JPS：虚拟机进程状况工具" class="headerlink" title="JPS：虚拟机进程状况工具"></a>JPS：虚拟机进程状况工具</h1><blockquote>
<p>jps(JVM Process Status Tool)，可以列出正在运行的虚拟机进程，并显示虚拟机执行主类(Main Class，Main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier).</p>
</blockquote>
<p>JPS主要用于查询LVMID来确定需要监控的是哪一个虚拟机进程。对于本地虚拟机进程，LVMID与操作系统的进程ID一致，但如果同时启动了多个虚拟机进程，无法根据进程名称定位时，就必须依赖jps命令显示主类的功能才能区分。</p>
<blockquote>
<p>jps命令格式：jps [ options ] [ hostid ]</p>
</blockquote>
<table>
<thead>
<tr>
<th>选项</th>
<th>作用</th>
</tr>
</thead>
<tbody><tr>
<td>-q</td>
<td>只输出LVMID，省略主类的名称</td>
</tr>
<tr>
<td>-m</td>
<td>输出虚拟机进程启动时传递给主类main()函数的参数</td>
</tr>
<tr>
<td>-l</td>
<td>输出主类的全名，如果进程执行的是jar包，则输出jar路径</td>
</tr>
<tr>
<td>-v</td>
<td>输出虚拟机进程启动时的JVM参数</td>
</tr>
</tbody></table>
<blockquote>
<p>JPS执行样例：</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/JVMjps.png" width=""></div>

<h1 id="jstat：虚拟机统计信息监视工具"><a href="#jstat：虚拟机统计信息监视工具" class="headerlink" title="jstat：虚拟机统计信息监视工具"></a>jstat：虚拟机统计信息监视工具</h1><blockquote>
<p>jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据，在没有GUI图形界面、只提供了纯文本控制台环境的服务器上，它将是运行期定位虚拟机性能问题的常用工具。</p>
</blockquote>
<blockquote>
<p>jstat命令格式为：jstat [ option vmid [ interval[s|ms] [count] ] ]</p>
</blockquote>
<table>
<thead>
<tr>
<th>选项</th>
<th>作用</th>
</tr>
</thead>
<tbody><tr>
<td>-class</td>
<td>监视类加载、卸载数量、总空间以及类装载所耗费的时间</td>
</tr>
<tr>
<td>-gc</td>
<td>监视Java堆状况，包括Eden区、2个Survivor区、老年代、永久代等的容量，已用空间，垃圾收集时间合计等信息</td>
</tr>
<tr>
<td>-gccapacity</td>
<td>监视内容与-gc基本相同，但输出主要关注Java堆各个区域使用到的最大，最小空间</td>
</tr>
<tr>
<td>-gcutil</td>
<td>监视内容与-gc基本相同，但输出主要关注已使用空间占总空间的百分比</td>
</tr>
<tr>
<td>-gccause</td>
<td>与-gcutil功能一样，但额外输出导致上一次垃圾收集产生的原因</td>
</tr>
<tr>
<td>-gcnew</td>
<td>监视新生代垃圾收集状况</td>
</tr>
<tr>
<td>-gcnewcapacity</td>
<td>监视内容与-gcnew基本相同，输出主要关注使用到的最大、最小空间</td>
</tr>
<tr>
<td>-gcold</td>
<td>监视老年代的收集状况</td>
</tr>
<tr>
<td>-gcoldcapacity</td>
<td>监视内容与-gcold基本相同，输出主要关注使用到的最大、最小空间</td>
</tr>
<tr>
<td>-gcpermcapacity</td>
<td>输出永久代使用到的最大、最小空间</td>
</tr>
<tr>
<td>-compiler</td>
<td>输出即时编译器编译过的方法、耗时等等信息</td>
</tr>
<tr>
<td>-printcompilation</td>
<td>输出已经被即时编译的方法</td>
</tr>
</tbody></table>
<blockquote>
<p>jstat执行样例：</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/JVMjstat.png" width=""></div>

<p>-gccause说明：</p>
<ul>
<li>新生代Eden区（E）使用了82.08%的空间，</li>
<li>S0（Survivor区）为空，S1占比41.42%，</li>
<li>老年代（O区）使用22.7%的空间，</li>
<li>压缩类空间和元数据空间分别占比92.9%和93.64%，</li>
<li>Minor GC次数一共发生215次，总耗时2.389s, Full GC触发4次，总耗时0.781s， 所有GC总耗时(GCT)为3.171s。</li>
</ul>
<blockquote>
<p>-gccause的LGCC(Last GC Cause)表示上一次GC发生原因为Allocation Failure，内存分配失败，如Eden区满导致Minor GC或老年代最大连续空间无法存入大对象导致Full GC。</p>
</blockquote>
<p>使用jstat工具在纯文本状态下监视虚拟机状态变化，在用户体验上不如JMC、VisualVM等可视化监视工具直接以图表形式展现的直观，但在实际生产环境中，多数服务器管理员已经习惯了在文本控制台工作，直接在控制台使用jstat命令依然是一种常用的监控方式。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JVM/" rel="tag">JVM</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-JVM-性能优化-Part-4-C4-垃圾回收"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/17/JVM-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-Part-4-C4-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/"
    >JVM性能优化,Chapter4:C4垃圾回收</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/17/JVM-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-Part-4-C4-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/" class="article-date">
  <time datetime="2020-07-17T02:45:38.000Z" itemprop="datePublished">2020-07-17</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/JVM/">JVM</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <p><img src="http://fahoud.gitee.io/hexo-dist/jvm/JVMlogo.jpg" alt="JVMlogo"></p>
<h1 id="JVM-性能优化-Part-4-C4-垃圾回收"><a href="#JVM-性能优化-Part-4-C4-垃圾回收" class="headerlink" title="JVM 性能优化, Part 4: C4 垃圾回收"></a>JVM 性能优化, Part 4: C4 垃圾回收</h1><blockquote>
<p>ImportNew注：本文是JVM性能优化 系列-第4篇。前3篇文章请参考文章结尾处的JVM优化系列文章。作为Eva Andreasson的JVM性能优化系列的第4篇，本文将对C4垃圾回收器进行介绍。使用C4垃圾回收器可以有效提升对低延迟有要求的企业级Java应用程序的伸缩性。</p>
</blockquote>
<p>到目前为止，本系列的文章将stop-the-world式的垃圾回收视为影响Java应用程序伸缩性的一大障碍，而伸缩性又是现代企业级Java应用程序开发的基础要求，因此这一问题亟待改善。幸运的是，针对此问题，JVM中已经出现了一些新特性，所使用的方式或是对stop-the-world式的垃圾回收做微调，或是消除冗长的暂停（这样更好些）。在一些多核系统中，内存不再是稀缺资源，因此，JVM的一些新特性就充分利用多核系统的潜在优势来增强Java应用程序的伸缩性。</p>
<p>在本文中，我将着重介绍C4算法，该算法是Azul System公司中无暂停垃圾回收算法的新成果，目前只在Zing JVM上得到实现。此外，本文还将对Oracle公司的G1垃圾回收算法和IBM公司的Balanced Garbage Collection Policy算法做简单介绍。希望通过对这些垃圾回收算法的学习可以扩展你对Java内存管理模型和Java应用程序伸缩性的理解，并激发你对这方面内容的兴趣以便更深入的学习相关知识。至少，你可以学习到在选择JVM时有哪些需要关注的方面，以及在不同应用程序场景下要注意的事项。</p>
<h2 id="C4算法中的并发性"><a href="#C4算法中的并发性" class="headerlink" title="C4算法中的并发性"></a>C4算法中的并发性</h2><p>Azul System公司的C4（Concurrent Continuously Compacting Collector，译者注，Azul官网给出的名字是Continuously Concurrent Compacting Collector）算法使用独一无二而又非常有趣的方法来实现低延迟的分代式垃圾回收。相比于大多数分代式垃圾回收器，C4的不同之处在于它认为垃圾回收并不是什么坏事（即应用程序产生垃圾很正常），而压缩是不可避免的。在设计之初，C4就是要牺牲各种动态内存管理的需求，以满足需要长时间运行的服务器端应用程序的需求。</p>
<p>C4算法将释放内存的过程从应用程序行为和内存分配速率中分离出来，并加以区分。这样就实现了并发运行，即应用程序可以持续运行，而不必等待垃圾回收的完成。其中的并发性是关键所在，正是由于并发性的存在才可以使暂停时间不受垃圾回收周期内堆上活动数据数量和需要跟踪与更新的引用数量的影响，将暂停时间保持在较低的水平。正如我在本系列第3篇中介绍的一样，大多数垃圾回收器在工作周期内都包含了stop-the-world式的压缩过程，这就是说应用程序的暂停时间会随活动数据总量和堆中对象间引用的复杂度的上升而增加。使用C4算法的垃圾回收器可以并发的执行压缩操作，即压缩与应用程序线程同时工作，从而解决了影响JVM伸缩性的最大难题。</p>
<p>实际上，为了实现并发性，C4算法改变了现代Java企业级架构和部署模型的基本假设。想象一下拥有数百GB内存的JVM会是什么样的：</p>
<ul>
<li><p>部署Java应用程序时，对伸缩性的要求无需要多个JVM配合，在单一JVM实例中即可完成。这时的部署是什么样呢？</p>
</li>
<li><p>有哪些以往因GC限制而无法在内存存储的对象？</p>
</li>
<li><p>那些分布式集群（如缓存服务器、区域服务器，或其他类型的服务器节点）会有什么变化？当可以增加JVM内存而不会对应用程序响应时间造成负面影响时，传统的节点数量、节点死亡和缓存丢失的计算会有什么变化呢？</p>
</li>
</ul>
<h2 id="C4算法的3的阶段"><a href="#C4算法的3的阶段" class="headerlink" title="C4算法的3的阶段"></a>C4算法的3的阶段</h2><p>C4算法的一个基本假设是“垃圾回收不是坏事”和“压缩不可避免”。C4算法的设计目标是实现垃圾回收的并发与协作，剔除stop-the-world式的垃圾回收。C4垃圾回收算法包含一下3个阶段：</p>
<blockquote>
<ol>
<li><i>标记（Marking）</i> — 找到活动对象</li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li><i>重定位（Relocation）</i> — 将存活对象移动到一起，以便可以释放较大的连续空间，这个阶段也可称为“压缩（compaction）”</li>
</ol>
</blockquote>
<blockquote>
<ol start="3">
<li><i>重映射（Remapping）</i> — 更新被移动的对象的引用。</li>
</ol>
</blockquote>
<p>下面的内容将对每个阶段做详细介绍。</p>
<h2 id="C4算法中的标记阶段"><a href="#C4算法中的标记阶段" class="headerlink" title="C4算法中的标记阶段"></a>C4算法中的标记阶段</h2><p>在C4算法中，<i>标记阶段（marking phase）</i>使用了<i>并发标记（concurrent marking）</i>和<i>引用跟踪（reference-tracing）</i>的方法来标记活动对象，这方面内容已经在本系列的第3篇中介绍过。</p>
<p>在标记阶段中，GC线程会从线程栈和寄存器中的活动对象开始，遍历所有的引用，标记找到的对象，这些GC线程会遍历堆上所有的<i>可达（reachable）</i>对象。在这个阶段，C4算法与其他并发标记器的工作方式非常相似。</p>
<p>C4算法的标记器与其他并发标记器的区别也是始于并发标记阶段的。在并发标记阶段中，如果应用程序线程修改未标记的对象，那么该对象会被放到一个队列中，以备遍历。这就保证了该对象最终会被标记，也因为如此，C4垃圾回收器或另一个应用程序线程不会重复遍历该对象。这样就节省了标记时间，消除了<i>递归重标记（recursive remark）</i>的风险。（注意，长时间的递归重标记有可能会使应用程序因无法获得足够的内存而抛出<b>OOM错误</b>，这也是大部分垃圾回收场景中的普遍问题。）</p>
<p><img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmperf4.png" alt="Figure 1"></p>
<center style="font-size:14px;color:#444043;text-decoration:none">Figure 1. Application threads traverse the heap just once during marking</center>


<p>如果C4算法的实现是基于<i>脏卡表（dirty-card tables）</i>或其他对已经遍历过的堆区域的读写操作进行记录的方法，那垃圾回收线程就需要重新访问这些区域做重标记。在极端条件下，垃圾回收线程会陷入到永无止境的重标记中 —— 至少这个过程可能会长到使应用程序因无法分配到新的内存而抛出OOM错误。但C4算法是基于LVB（load value barrier）实现的，LVB具有自愈能力，可以使应用程序线程迅速查明某个引用是否已经被标记过了。如果这个引用没有被标记过，那么应用程序会将其添加到GC队列中。一旦该引用被放入到队列中，它就不会再被重标记了。应用程序线程可以继续做它自己的事。</p>
<blockquote>
<p><i>脏对象（dirty object）和卡表（card table）</i></p>
</blockquote>
<p>由于某些原因（例如在一个并发垃圾回收周期中，对象被修改了），垃圾回收器需要重新访问某些对象，那么这些对象脏对象（dirty object）。这这些脏对象，或堆中脏区域的引用，通过会记录在一个专门的数据结构中，这就是卡表。</p>
<p>在C4算法中，并没有<i>重标记（re-marking）</i>这个阶段，在第一次便利整个堆时就会将所有可达对象做标记。因为运行时不需要做重标记，也就不会陷入无限循环的重标记陷阱中，由此而降低了应用程序因无法分配到内存而抛出OOM错误的风险。</p>
<h2 id="C4算法中的重定位-——-应用程序线程与GC的协作"><a href="#C4算法中的重定位-——-应用程序线程与GC的协作" class="headerlink" title="C4算法中的重定位 ——　应用程序线程与GC的协作"></a>C4算法中的重定位 ——　应用程序线程与GC的协作</h2><p>C4算法中，<i>重定位阶段（reloacation phase）</i>是由GC线程和应用程序线程以协作的方式，并发完成的。这是因为GC线程和应用程序线程会同时工作，而且无论哪个线程先访问将被移动的对象，都会以协作的方式帮助完成该对象的移动任务。因此，应用程序线程可以继续执行自己的任务，而不必等待整个垃圾回收周期的完成。</p>
<p>正如Figure 2所示，碎片内存页中的活动对象会被重定位。在这个例子中，应用程序线程先访问了要被移动的对象，那么应用程序线程也会帮助完成移动该对象的工作的初始部分，这样，它就可以很快的继续做自己的任务。虚拟地址（指相关引用）可以指向新的正确位置，内存也可以快速回收。</p>
<p><img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmperf5.png" alt="Figure 2"></p>
<center style="font-size:14px;color:#444043;text-decoration:none">Figure 2. A page selected for relocation and the empty new page that it will be moved to</center>


<p>如果是GC线程先访问到了将被移动的对象，那事情就简单多了，GC线程会执行移动操作的。如果在<i>重映射阶段（re-mapping phase，后续会提到）</i>也访问这个对象，那么它必须检查该对象是否是要被移动的。如果是，那么应用程序线程会重新定位这个对象的位置，以便可以继续完成自己任务。（对大对象的移动是通过将该对象打碎再移动完成的。如果你对这部分内容感兴趣的话，推荐你阅读一下相关资源中的这篇白皮书“C4: The Continuously Concurrent Compacting Collector”）</p>
<p>当所有的活动对象都从某个内存也中移出后，剩下的就都是垃圾数据了，这个内存页也就可以被整体回收了。正如Figure 2中所示。</p>
<blockquote>
<p>关于清理</p>
</blockquote>
<p>在C4算法中并没有清理阶段（sweep phase），因此也就不需要这个在大多数垃圾回收算法中比较常用的操作。在指向被移动的对象的引用都更新为指向新的位置之前，from页中的虚拟地址空间必须被完整保留。所以C4算法的实现保证了，在所有指向这个页的引用处于稳定状态前，所有的虚拟地址空间都会被锁定。然后，算法会立即回收物理内存页。</p>
<p>很明显，无需执行stop-the-world式的移动对象是有很大好处的。由于在重定位阶段，所有活动对象都是并发移动的，因此它们可以被更有效率的放入到相邻的地址中，并且可以充分的压缩。通过并发执行重定位操作，堆被压缩为连续空间，也无需挂起所有的应用程序线程。这种方式消除了Java应用程序访问内存的传统限制（更多关于Java应用程序内存模型的内容参见ImportNew编译整理的第一篇《<a href="http://www.importnew.com/1774.html" target="_blank" rel="noopener">JVM性能优化， Part 1 ―― JVM简介</a>》）。</p>
<p>经过上述的过程后，如何更新引用呢？如何实现一个非stop-the-world式的操作呢？</p>
<h2 id="C4算法中的重映射"><a href="#C4算法中的重映射" class="headerlink" title="C4算法中的重映射"></a>C4算法中的重映射</h2><p>在重定位阶段，某些指向被移动的对象的引用会自动更新。但是，在重定位阶段，那些指向了被移动的对象的引用并没有更新，仍然指向原处，所以它们需要在后续完成更新操作。C4算法中的重映射阶段（re-mapping phase）负责完成对那些活动对象已经移出，但仍指向那些的引用进行更新。当然，重映射也是一个协作式的并发操作。</p>
<p>Figure 3中，在重定位阶段，活动对象已经被移动到了一个新的内存页中。在重定位之后，GC线程立即开始更新那些仍然指向之前的虚拟地址空间的引用，将它们指向那些被移动的对象的新地址。垃圾回收器会一直执行此项任务，直到所有的引用都被更新，这样原先虚拟内存空间就可以被整体回收了。</p>
<p><img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmperf6.png" alt="Figure 3"></p>
<center style="font-size:14px;color:#444043;text-decoration:none">Figure 3. Whatever thread finds an invalid address enables an update to the correct new address</center>


<p>但如果在GC完成对所有引用的更新之前，应用程序线程想要访问这些引用的话，会出现什么情况呢？在C4算法中，应用程序线程可以很方便的帮助完成对引用进行更新的工作。如果在重映射阶段，应用程序线程访问了处于非稳定状态的引用，它会找到该引用的正确指向。如果应用程序线程找到了正确的引用，它会更新该引用的指向。当完成更新后，应用程序线程会继续自己的工作。</p>
<p>协作式的重映射保证了引用只会被更新一次，该引用下的子引用也都可以指向正确的新地址。此外，在大多数其他GC实现中，引用指向的地址不会被存储在该对象被移动之前的位置；相反，这些地址被存储在一个堆外结构（off-heap structure）中。这样，无需在对所有引用的更新完成之前，再花费精力保持整个内存页完好无损，这个内存页可以被整体回收。</p>
<h2 id="C4算法真的是无暂停的么？"><a href="#C4算法真的是无暂停的么？" class="headerlink" title="C4算法真的是无暂停的么？"></a>C4算法真的是无暂停的么？</h2><p>在C4算法的重映射阶段，正在跟踪引用的线程仅会被中断一次，而这次中断仅仅会持续到对该引用的检索和更新完成，在这次中断后，线程会继续运行。相比于其他并发算法来说，这种实现会带来巨大的性能提升，因为其他的并发立即回收算法需要等到每个线程都运行到一个<i>安全点（safe point）</i>，然后同时挂起所有线程，再开始对所有的引用进行更新，完成后再恢复所有线程的运行。</p>
<p>对于并发压缩垃圾回收器来说，由于垃圾回收所引起的暂停从来都不是问题。在C4算法的重定位阶段中，也不会有再出现更糟的碎片化场景了。实现了C4算法的垃圾回收器也不会出现背靠背（back-to-back）式的垃圾回收周期，或者是因垃圾回收而使应用程序暂停数秒甚至数分钟。如果你曾经体验过这种stop-the-world式的垃圾回收，那么很有可能是你给应用程序设置的内存太小了。你可以试用一下实现了C4算法的垃圾回收器，并为其分配足够多的内存，而完全不必担心暂停时间过长的问题。</p>
<h2 id="评估C4算法和其他可选方案"><a href="#评估C4算法和其他可选方案" class="headerlink" title="评估C4算法和其他可选方案"></a>评估C4算法和其他可选方案</h2><p>像往常一样，你需要针对应用程序的需求选择一款JVM和垃圾回收器。C4算法在设计之初就是无论堆中活动数据有多少，只要应用程序还有足够的内存可用，暂停时间都始终保持在较低的水平。正因如此，对于那些有大量内存可用，而对响应时间比较敏感的应用程来说，选择实现了C4算法的垃圾回收器正是不二之选。</p>
<p>而对于那些要求快速启动，内存有限的客户端应用程序来说，C4就不是那么适用。而对于那些对吞吐量有较高要求的应用程序来说，C4也并不适用。真正能够发挥C4威力的是那些为了提升应用程序工作负载而在每台服务器上部署了4到16个JVM实例的场景。此外，如果你经常要对垃圾回收器做调优的话，那么不妨考虑一下使用C4算法。综上所述，当响应时间比吞吐量占有更高的优先级时，C4是个不错的选择。而对那些不能接受长时间暂停的应用程序来说，C4是个理想的选择。</p>
<p>如果你正考虑在生产环境中使用C4，那么你可能还需要重新考虑一下如何部署应用程序。例如，不必为每个服务器配置16个具有2GB堆的JVM实例，而是使用一个64GB的JVM实例（或者增加一个作为热备份）。C4需要尽可能大的内存来保证始终有一个空闲内存页来为新创建的对象分配内存。（记住，内存不再是昂贵的资源了！）</p>
<p>如果你没有64GB，128GB，或1TB（或更多）内存可用，那么分布式的多JVM部署可能是一个更好的选择。在这种场景中，你可以考虑使用Oracle HotSpot JVM的G1垃圾回收器，或者IBM JVM的平衡垃圾回收策略（Balanced Garbage Collection Policy）。下面将对这两种垃圾回收器做简单介绍。</p>
<h2 id="Gargabe-First-（G1）-垃圾回收器"><a href="#Gargabe-First-（G1）-垃圾回收器" class="headerlink" title="Gargabe-First （G1） 垃圾回收器"></a>Gargabe-First （G1） 垃圾回收器</h2><p>G1垃圾回收器是新近才出现的垃圾回收器，是Oracle HotSpot JVM的一部分，在最近的JDK1.6版本中首次出现（译者注，该文章写于2012-07-11）。在启动Oracle JDK时附加命令行选项-XX:+UseG1GC，可以启动G1垃圾回收器。</p>
<p>与C4类似，这款标记-清理（mark-and-sweep）垃圾回收器也可作为对低延迟有要求的应用程序的备选方案。G1算法将堆分为固定大小区域，垃圾回收会作用于其中的某些区域。在应用程序线程运行的同时，启用后台线程，并发的完成标记工作。这点与其他并发标记算法相似。</p>
<p>G1增量方法可以使暂停时间更短，但更频繁，而这对一些力求避免长时间暂停的应用程序来说已经足够了。另一方面，正如在本系列的[Part 3][4]中介绍的，使用G1垃圾回收器需要针对应用程序的实际需求做长时间的调优，而其GC中断又是stop-the-world式的。所以对那些对低延迟有很高要求的应用程序来说，G1并不是一个好的选择。进一步说，从暂停时间总长来看，G1长于CMS（Oracle JVM中广为人知的并发垃圾回收器）。</p>
<p>G1使用拷贝算法（在Part 3中介绍过）完成部分垃圾回收任务。这样，每次垃圾回收器后，都会产生完全可用的空闲空间。G1垃圾回收器定义了一些区域的集合作为年轻代，剩下的作为老年代。</p>
<p>G1已经吸引了足够多的注意，引起了不小的轰动，但是它真正的挑战在于如何应对现实世界的需求。正确的调优就是其中一个挑战 —— 回忆一下，对于动态应用程序负载来说，没有永远“正确的调优”。一个问题是如何处理与分区大小相近的大对象，因为剩余的空间会成为碎片而无法使用。还有一个性能问题始终困扰着低延迟垃圾回收器，那就是垃圾回收器必须管理额外的数据结构。就我来说，使用G1的关键问题在于如何解决stop-the-world式垃圾回收器引起的暂停。Stop-the-world式的垃圾回收引起的暂停使任何垃圾回收器的能力都受制于堆大小和活动数据数量的增长，对企业级Java应用程序的伸缩性来说是一大困扰。</p>
<h2 id="IBM-JVM的平衡垃圾回收策略（Balanced-Garbage-Collection-Policy）"><a href="#IBM-JVM的平衡垃圾回收策略（Balanced-Garbage-Collection-Policy）" class="headerlink" title="IBM JVM的平衡垃圾回收策略（Balanced Garbage Collection Policy）"></a>IBM JVM的平衡垃圾回收策略（Balanced Garbage Collection Policy）</h2><p>IBM JVM的平衡垃圾回收（Balanced Garbage Collection BGC）策略通过在启动IBM JDK时指定命令行选项-Xgcpolicy:balanced来启用。乍一看，BGC很像G1，它也是将Java堆划分成相同大小的空间，称为区间（region），执行垃圾回收时会对每个区间单独回收。为了达到最佳性能，在选择要执行垃圾回收的区间时使用了一些启发性算法。BGC中关于代的划分也与G1相似。</p>
<p>IBM的平衡垃圾回收策略仅在64位平台得到实现，是一种NUMA架构（Non-Uniform Memory Architecture），设计之初是为了用于具有4GB以上堆的应用程序。由于拷贝算法或压缩算法的需要，BGC的部分垃圾回收工作是stop-the-world式的，并非完全并发完成。所以，归根结底，BGC也会遇到与G1和其他没有实现并发压缩选法的垃圾回收器相似的问题。</p>
<h3 id="结论：回顾"><a href="#结论：回顾" class="headerlink" title="结论：回顾"></a>结论：回顾</h3><p>C4是基于引用跟踪的、分代式的、并发的、协作式垃圾回收算法，目前只在Azul System公司的Zing JVM得到实现。C4算法的真正价值在于：</p>
<blockquote>
<p>消除了重标记可能引起的重标记无限循环，也就消除了在标记阶段出现OOM错误的风险。</p>
</blockquote>
<blockquote>
<p>压缩，以自动、且不断重定位的方式消除了固有限制：堆中活动数据越多，压缩所引起的暂停越长。</p>
</blockquote>
<blockquote>
<p>垃圾回收不再是stop-the-world式的，大大降低垃圾回收对应用程序响应时间造成的影响。</p>
</blockquote>
<blockquote>
<p>没有了清理阶段，降低了在完成GC之前就因为空闲内存不足而出现OOM错误的风险。</p>
</blockquote>
<blockquote>
<p>内存可以以页为单位立即回收，使那些需要使用较多内存的Java应用程序有足够的内存可用。</p>
</blockquote>
<p>并发压缩是C4独一无二的优势。使应用程序线程GC线程协作运行，保证了应用程序不会因GC而被阻塞。C4将内存分配和提供足够连续空闲内存的能力完全区分开。C4使你可以为JVM实例分配尽可能大的内存，而无需为应用程序暂停而烦恼。使用得当的话，这将是JVM技术的一项革新，它可以借助于当今的多核、TB级内存的硬件优势，大大提升低延迟Java应用程序的运行速度。</p>
<p>如果你不介意一遍又一遍的调优，以及频繁的重启的话，如果你的应用程序适用于水平部署模型的话（即部署几百个小堆JVM实例而不是几个大堆JVM实例），G1也是个不错的选择。</p>
<p>对于<i>动态低延迟启发性自适应（dynamic low-latency heuristic adaption）</i>算法而言，BGC是一项革新，JVM研究者对此算法已经研究了几十年。该算法可以应用于较大的堆。而<i>动态自调优算法（ dynamic self-tuning algorithm）</i>的缺陷是，它无法跟上突然出现的负载高峰。那时，你将不得不面对最糟糕的场景，并根据实际情况再分配相关资源。</p>
<p>最后，为你的应用程序选择最适合的JVM和垃圾回收器时，最重要的考虑因素是应用程序中吞吐量和暂停时间的优先级次序。你想把时间和金钱花在哪？从纯粹的技术角度说，基于我十年来对垃圾回收的经验，我一直在寻找更多关于并发压缩的革新性技术，或其他可以以较小代价完成移动对象或重定位的方法。我想影响企业级Java应用程序伸缩性的关键就在于并发性。</p>
<p><b>JVM 性能优化系列</p>
<p>第一篇 《<a href="http://www.importnew.com/1774.html" target="_blank" rel="noopener">JVM性能优化， Part 1 ―― JVM简介</a> 》</p>
<p>第二篇《<a href="http://www.importnew.com/2009.html" target="_blank" rel="noopener">JVM性能优化， Part 2 ―― 编译器</a>》</p>
<p>第三篇《<a href="http://www.importnew.com/2233.html" target="_blank" rel="noopener">JVM性能优化， Part 3 —— 垃圾回收</a>》</b></p>
<p><i>原文链接： <a href="https://www.infoworld.com/article/2078661/jvm-performance-optimization--part-4--c4-garbage-collection-for-low-latency-java-ap.html" target="_blank" rel="noopener">javaworld</a> 翻译： <a href="www.importnew.com">ImportNew.com</a> - <a href="http://www.importnew.com/author/caoxudong" target="_blank" rel="noopener">曹旭东</a><br>译文链接： <a href="http://www.importnew.com/2410.html" target="_blank" rel="noopener">http://www.importnew.com/2410.html</a></i></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JVM/" rel="tag">JVM</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-JVM-MinorGC和FullGC触发条件"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/17/JVM-MinorGC%E5%92%8CFullGC%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6/"
    >JVM MinorGC和FullGC触发条件</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/17/JVM-MinorGC%E5%92%8CFullGC%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6/" class="article-date">
  <time datetime="2020-07-17T02:45:38.000Z" itemprop="datePublished">2020-07-17</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/JVM/">JVM</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="Java垃圾回收机制"><a href="#Java垃圾回收机制" class="headerlink" title="Java垃圾回收机制"></a>Java垃圾回收机制</h1><p>GC，即就是Java垃圾回收机制。目前主流的JVM（HotSpot）采用的是分代收集算法。作为Java开发者，一般不需要专门编写内存回收和垃圾清理代码，对内存泄露和溢出的问题。与C++不同的是，Java采用的是类似于树形结构的可达性分析法来判断对象是否还存在引用。即：从gcroot开始，把所有可以搜索得到的对象标记为存活对象。</p>
<blockquote>
<p>具体过程：</p>
</blockquote>
<p>当GC线程启动时，会通过可达性分析法把Eden区和From Survivor区的存活对象复制到To Survivor区，然后把Eden Space和From Survivor区的对象释放掉。当GC轮训扫描To Survivor区一定次数后，把依然存活的对象复制到老年代，然后释放To Survivor区的对象。</p>
<p>对于用可达性分析法搜索不到的对象，GC并不一定会回收该对象。要完全回收一个对象，至少需要经过两次标记的过程：</p>
<ul>
<li>第一次标记：对于一个没有其他引用的对象，筛选该对象是否有必要执行finalize()方法，如果没有执行必要，则意味可直接回收。</li>
</ul>
<blockquote>
<p>（筛选依据：是否复写或执行过finalize()方法；因为finalize方法只能被执行一次）。</p>
</blockquote>
<ul>
<li>第二次标记：如果被筛选判定位有必要执行，则会放入FQueue队列，并自动创建一个低优先级的finalize线程来执行释放操作。如果在一个对象释放前被其他对象引用，则该对象会被移除FQueue队列。</li>
</ul>
<blockquote>
<p>finalize()只能执行一次，意味着对象只能自救一次，触发下一次gc是若该对象未被标记则会直接清除</p>
</blockquote>
<h1 id="虚拟机中GC的过程"><a href="#虚拟机中GC的过程" class="headerlink" title="虚拟机中GC的过程"></a>虚拟机中GC的过程</h1><blockquote>
<p>在初始阶段，新创建的对象被分配到Eden区，survivor的两块空间都为空。</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc1.png" width=""></div>

<blockquote>
<p>当Eden区满了的时候，触发MinorGC</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc2.png" width=""></div>

<blockquote>
<p>经过扫描与标记，未被标记的对象从Eden区清除，存活的对象复制或移动到From Survivor区</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc3.png" width=""></div>

<blockquote>
<p> 在下一次的Minor GC中，Eden区的情况和上面一致，没有引用的对象被回收，存活的对象被复制到From survivor区。然而在From Survivor区，From Survivor的所有的数据都被复制到To Survivor，需要注意的是，在上次minor GC过程中移动到From Survivor中的两个对象在复制到To Survivor后其年龄要加1。此时Eden区From Survivor区被清空，所有存活的数据都复制到了To Survivor区，并且To Survivor区存在着年龄不一样的对象</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc4.png" width=""></div>

<blockquote>
<p>再下一次MinorGC则重复这个过程，这一次survivor的两个区对换，存活的对象被复制到S0，存活的对象年龄加1，Eden区和另一个survivor区被清空</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc5.png" width=""></div>

<blockquote>
<p>再经过几次Minor GC之后，当存活对象的年龄达到一个阈值之后（可通过参数配置，默认是8），就会被从年轻代Promotion到老年代</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc6.png" width=""></div>

<blockquote>
<p>随着MinorGC一次又一次的进行，不断会有新的对象被promote到老年代</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc7.png" width=""></div>

<blockquote>
<p>上面基本上覆盖了整个年轻代所有的回收过程。最终，MajorGC将会在老年代发生，老年代的空间将会被清除和压缩</p>
</blockquote>
<div align=center>
<img src="http://fahoud.gitee.io/hexo-dist/jvm/jvmgc8.png" width=""></div>

<p>从上面的过程可以看出，Eden区是连续的空间，且Survivor总有一个为空。经过一次GC和复制，一个Survivor中保存着当前还活着的对象，而Eden区和另一个Survivor区的内容都不再需要了，可以直接清空，到下一次GC时，两个Survivor的角色再互换。因此，这种方式分配内存和清理内存的效率都极高，这种垃圾回收的方式就是著名的“停止-复制（Stop-and-copy）”清理法（将Eden区和一个Survivor中仍然存活的对象拷贝到另一个Survivor中），这不代表着停止复制清理法很高效，其实，它也只在这种情况下（基于大部分对象存活周期很短的事实）高效，如果在老年代采用停止复制，则是非常不合适的。</p>
<p>老年代存储的对象比年轻代多得多，而且不乏大对象，对老年代进行内存清理时，如果使用停止-复制算法，则相当低效。一般，老年代用的算法是标记-压缩算法，即：标记出仍然存活的对象（存在引用的），将所有存活的对象向一端移动(<i>标记-整理算法</i>)，以保证内存的连续。</p>
<p>在发生Minor GC时，虚拟机会检查每次晋升进入老年代的大小是否大于老年代的剩余空间大小，如果大于，则直接触发一次Full GC，否则，就查看是否设置了-XX:+HandlePromotionFailure（允许担保失败），如果允许，则只会进行MinorGC，此时可以容忍内存分配失败；如果不允许，则仍然进行Full GC（这代表着如果设置-XX:+Handle PromotionFailure，则触发MinorGC就会同时触发Full GC，哪怕老年代还有很多内存，所以，最好不要这样做）。</p>
<p>关于方法区即永久代的回收，永久代的回收有两种：常量池中的常量，无用的类信息，常量的回收很简单，没有引用了就可以被回收。对于无用的类进行回收，必须保证3点：</p>
<blockquote>
<ol>
<li>类的所有实例都已经被回收。</li>
<li>加载类的ClassLoader已经被回收。</li>
<li>类对象的Class对象没有被引用（即没有通过反射引用该类的地方）。</li>
</ol>
</blockquote>
<p>永久代的回收并不是必须的，可以通过参数来设置是否对类进行回收。</p>
<h1 id="Minor-GC-，Full-GC-触发条件"><a href="#Minor-GC-，Full-GC-触发条件" class="headerlink" title="Minor GC ，Full GC 触发条件"></a>Minor GC ，Full GC 触发条件</h1><h2 id="Minor-GC触发条件："><a href="#Minor-GC触发条件：" class="headerlink" title="Minor GC触发条件："></a>Minor GC触发条件：</h2><blockquote>
<p>当Eden区满时，触发Minor GC。</p>
</blockquote>
<h2 id="Full-GC触发条件："><a href="#Full-GC触发条件：" class="headerlink" title="Full GC触发条件："></a>Full GC触发条件：</h2><blockquote>
<p>（1）调用System.gc时，系统建议执行Full GC，但是不必然执行<br>（2）老年代空间不足<br>（3）方法去空间不足<br>（4）通过Minor GC后进入老年代的平均大小大于老年代的可用内存<br>（5）由Eden区、From Space区向To Space区复制时，对象大小大于To Space可用内存，则把该对象转存到老年代，且老年代的可用内存小于该对象大小。</p>
</blockquote>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JVM/" rel="tag">JVM</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-git博客分支管理"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/13/git%E5%8D%9A%E5%AE%A2%E5%88%86%E6%94%AF%E7%AE%A1%E7%90%86/"
    >git博客分支管理</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/13/git%E5%8D%9A%E5%AE%A2%E5%88%86%E6%94%AF%E7%AE%A1%E7%90%86/" class="article-date">
  <time datetime="2020-07-13T02:47:40.000Z" itemprop="datePublished">2020-07-13</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/Git/">Git</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="git-hexo部署到github的源码管理"><a href="#git-hexo部署到github的源码管理" class="headerlink" title="git hexo部署到github的源码管理"></a>git hexo部署到github的源码管理</h1><h2 id="master用于部署hexo"><a href="#master用于部署hexo" class="headerlink" title="master用于部署hexo"></a>master用于部署hexo</h2><blockquote>
<p>每次写完文档后，不希望把所有数据都保存到服务器中，希望把源码和数据都保存到github中。<br>因为github规定了部署博客必需为master分支，所以用master部署hexo，用dev存储整个hexo文件夹</p>
</blockquote>
<blockquote>
<p>实现hexo d先执行以下命令安装插件</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure>


<blockquote>
<p>_config.yml文件中部署配置</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">deploy:</span><br><span class="line">  <span class="built_in">type</span>: git</span><br><span class="line">  repo:  </span><br><span class="line">    gitee: https://gitee.com/Fahoud/Fahoud.git</span><br><span class="line">    github: https://github.com/Fahoud/fahoud.github.io.git</span><br><span class="line">  branch: master</span><br></pre></td></tr></table></figure>

<blockquote>
<p>更改文档内容或者主题设置后部署到github或者码云</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ hexo claen</span><br><span class="line">$ hexo g</span><br><span class="line">$ hexo d</span><br></pre></td></tr></table></figure>

<h2 id="dev分支存储源码"><a href="#dev分支存储源码" class="headerlink" title="dev分支存储源码"></a>dev分支存储源码</h2><blockquote>
<p>本地只有一条分支，为master</p>
</blockquote>
<blockquote>
<p>把整个hexo文件夹推送到github或者码云</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ git add .     <span class="comment">#add所有更改</span></span><br><span class="line">$ git commit -m <span class="string">'Update xxx'</span>   <span class="comment">#commit</span></span><br><span class="line">$ git push github master:dev    <span class="comment">#推上github的dev分支</span></span><br><span class="line">$ </span><br><span class="line">$ git add . </span><br><span class="line">$ git commit -m <span class="string">'Update xxx'</span></span><br><span class="line">$ git push gitee master:hexo   <span class="comment">#推上gitee的hexo分支</span></span><br></pre></td></tr></table></figure>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Git/" rel="tag">Git</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-git-pull之后merging冲突解决"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/12/git-pull%E4%B9%8B%E5%90%8Emerging%E5%86%B2%E7%AA%81%E8%A7%A3%E5%86%B3/"
    >git pull之后merging冲突解决</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/12/git-pull%E4%B9%8B%E5%90%8Emerging%E5%86%B2%E7%AA%81%E8%A7%A3%E5%86%B3/" class="article-date">
  <time datetime="2020-07-12T06:15:29.000Z" itemprop="datePublished">2020-07-12</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/Git/">Git</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="解决merging冲突"><a href="#解决merging冲突" class="headerlink" title="解决merging冲突"></a>解决merging冲突</h1>
        <div id="aplayer-RBZqAkyN" class="aplayer aplayer-tag-marker" style="margin-bottom: 20px;">
            <pre class="aplayer-lrc-content"></pre>
        </div>
        <script>
          var ap = new APlayer({
            element: document.getElementById("aplayer-RBZqAkyN"),
            narrow: false,
            autoplay: false,
            showlrc: false,
            music: {
              title: "BECAUSE",
              author: "小林未郁",
              url: "小林未郁_BECAUSE.mp3",
              pic: "https://fahoud.gitee.io/hexo-dist/music/mika_Because.jpg",
              lrc: ""
            }
          });
          window.aplayers || (window.aplayers = []);
          window.aplayers.push(ap);
        </script>

<h2 id="一、出现merging冲突的原因"><a href="#一、出现merging冲突的原因" class="headerlink" title="一、出现merging冲突的原因"></a>一、出现merging冲突的原因</h2><blockquote>
<p>git远程上存在一个本地不存在的git 分支，就是本地远程代码不同步</p>
</blockquote>
<h2 id="二、解决方式"><a href="#二、解决方式" class="headerlink" title="二、解决方式:"></a>二、解决方式:</h2><h3 id="方法一："><a href="#方法一：" class="headerlink" title="方法一："></a>方法一：</h3><ul>
<li>git pull 出现冲突后可以暂存本地修改git stash ,然后git pull 更新代码，</li>
<li>git stash list 可查看暂存记录列表，释放本地暂存 </li>
<li>git stash apply stash@{0} ,出现冲突文件，找到并解决，</li>
<li>然后可以提交git add . 加入索引库，然后本地提交git commit -m ‘注释’ 最后git push到远程</li>
</ul>
<h3 id="方法二："><a href="#方法二：" class="headerlink" title="方法二："></a>方法二：</h3><ul>
<li><p>1.git pull  更新代码，发现:</p>
<blockquote>
<p>error: Your local changes to the following files would be overwritten by merge:pom.xml<br>Please commit your changes or stash them before you merge.</p>
</blockquote>
</li>
<li><p>这说明你的pom.xml与远程有冲突，你需要先提交本地的修改然后更新。</p>
</li>
<li><p>2.git add pom.xml   git commit -m ‘冲突解决’    提交本地的pom.xml文件，不进行推送远程</p>
</li>
<li><p>3.git pull   更新代码</p>
<blockquote>
<p>Auto-merging pom.xml<br>CONFLICT (content): Merge conflict in pom.xml<br>Automatic merge failed; fix conflicts and then commit the result.</p>
</blockquote>
</li>
<li><p>更新后你的本地分支上会出现 (develop|MERGING)类似这种标志</p>
</li>
<li><p>4.找到你本地的pom.xml文件，并打开</p>
<blockquote>
<p>你会在文件中发现&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD ，=======  ，&gt;&gt;&gt;&gt;&gt;&gt;&gt; ae9a0f6b7e42fda2ce9b14a21a7a03cfc5344d61 这种标记，&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD和=======中间的是你自己的代码，  =======  和&gt;&gt;&gt;&gt;&gt;&gt;&gt;中间的是其他人修改的代码 自己确定保留那一部分代码，最后删除&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD ，=======  ，&gt;&gt;&gt;&gt;&gt;&gt;&gt;这种标志</p>
</blockquote>
</li>
<li><p>5.git add pom.xml    git commit -m ‘冲突解决结束’   再次将本地的pom.xml文件提交</p>
</li>
<li><p>6.git push   将解决冲突后的文件推送到远程</p>
</li>
</ul>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Git/" rel="tag">Git</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-我的第一篇文章"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/12/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0/"
    >我的第一篇文章</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/12/%E6%88%91%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0/" class="article-date">
  <time datetime="2020-07-12T01:49:00.000Z" itemprop="datePublished">2020-07-12</time>
</a>    
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="第一次搞博客"><a href="#第一次搞博客" class="headerlink" title="第一次搞博客"></a>第一次搞博客</h1><p>BGM：小林未郁-BECAUSE</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-hello-world"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/07/12/hello-world/"
    >Hello World</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/07/12/hello-world/" class="article-date">
  <time datetime="2020-07-12T01:39:19.900Z" itemprop="datePublished">2020-07-12</time>
</a>    
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p>
<h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p>
<h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p>
<h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p>
<h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure>

<p>More info: <a href="https://hexo.io/docs/one-command-deployment.html" target="_blank" rel="noopener">Deployment</a></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
    </footer>
  </div>

    
 
   
</article>

    
  </article>
  

  
</section>
</div>

      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2020
        <i class="ri-heart-fill heart_icon"></i> Fahoud Du
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        Powered by <a href="https://hexo.io" target="_blank">Hexo</a>
        <span class="division">|</span>
        Theme - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>Visitors:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>Views:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="Fahoud"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/photos">照片</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="https://github.com/Fahoud" target="_blank" rel="noopener">Github</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="https://gitee.com/Fahoud" target="_blank" rel="noopener">Gitee</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="http://fahoud.gitee.io/hexo-dist/other/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="http://fahoud.gitee.io/hexo-dist/other/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
    <div id="music">
    
    
    
    <iframe frameborder="no" border="1" marginwidth="0" marginheight="0" width="200" height="52"
        src="//music.163.com/outchain/player?type=2&id=1349892027&auto=1&height=32"></iframe>
</div>

<style>
    #music {
        position: fixed;
        right: 15px;
        bottom: 0;
        z-index: 998;
    }
</style>
    
  </div>
<script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/","tagMode":false});</script></body>

</html>